using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Mail;
using Decav.Security;
using Decav.Gatsb.Data;
using Decav.Gatsb.Configuration;
using Decav.Reality;
using System.Threading;
using System.IO;
using Decav.Gatsb.Security;
using System.Diagnostics;

namespace Decav.Gatsb.Application
{
    /// <summary>
    /// The application instance for a Gatsb service application that recieves requests from a web services or remoting.
    /// </summary>
    public class GatsbServiceApplication : GatsbApplication
    {
        /// <summary>
        /// Gets the current <see cref="GatsbServiceApplication"/> instance.
        /// </summary>
        public static new GatsbServiceApplication Current
        {
            get
            {
                return (GatsbServiceApplication)GatsbApplication.Current;
            }
        }

        /// <summary>
        /// Creates a new unauthenticated application instance.
        /// </summary>
        public static void CreateNewThreadApplication()
        {
            GatsbServiceApplicationProvider.Current.CreateNewInstance();
        }

        /// <summary>
        /// Creates a new application instance on the current thread for the user (thread static).
        /// </summary>
        /// <param name="mobileNumber">The mobile phone number to create an application instance for.</param>
        /// <param name="smsEmail">The SMS address that is used as an alternate to the standard "{telnum}@vtext.com"
        /// style address.</param>
        public static void CreateNewThreadApplication(PhoneNumber mobileNumber, EmailAddress smsEmail)
        {
            if (mobileNumber == null)
                throw new ArgumentNullException("mobileNumber");


            UserLoginResult result = null;
            using (IGatsbDataAdapter adapt = new GatsbSqlDataAdapter())
                result = adapt.AuthenticateUser(mobileNumber, smsEmail);

            if (!result.Authenticated)
                throw new AuthenticationException(string.Format("Could not authenticate the user with the telephone number of '{0}'.", mobileNumber));

            GatsbServiceApplicationProvider.Current.CreateNewInstance();
            Current.CurrentUser = result.User;

            Thread.CurrentPrincipal = new GatsbPrincipal(new GatsbIdentity(result.User.Id, result.User.Email.ToString()));
        }

        /// <summary>
        /// Sends an SMS message to a user's telephone.
        /// </summary>
        /// <param name="recipientUserId">The recipient of the message.</param>
        /// <param name="senderAddress">The address to send to.</param>
        /// <param name="body">The message body to send.</param>
        public override void SendUserSms(User recipientUser, EmailAddress senderAddress, string body)
        {
            if (recipientUser == null)
                throw new ArgumentNullException("recipientUser");

            if (body == null)
                throw new ArgumentNullException("body");

            if (senderAddress == EmailAddress.Empty)
                senderAddress = new EmailAddress(GatsbConfigurationSection.Current.NotificationFromAddress);


            EmailAddress recipientAddress = (recipientUser.AlternateSmsAddress != EmailAddress.Empty) ?
                recipientUser.AlternateSmsAddress :
                recipientUser.TelephoneServiceProvider.CreateSmsAddress(recipientUser.MobilePhoneNumber);

#if !DEBUG

            SmtpClient client = new SmtpClient(GatsbConfigurationSection.Current.SmtpServer);
            client.Send(senderAddress.ToString(),
                recipientAddress.ToString(),
                string.Empty,
                body);

#else

            Console.WriteLine("Sending message to {0} from {1}", recipientAddress, senderAddress);
            Console.WriteLine();
            Console.WriteLine(body);

#endif
        }

        public override void AddPostComment(PostComment comment)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Sets the users current location to the specified location.
        /// </summary>
        /// <param name="locationId">The ID of the location to set the user as in.</param>
        public override void SetUserLocation(Guid location)
        {
            using (IGatsbDataAdapter adapt = GetDataAdapter())
                adapt.SetUserLocation(CurrentUser.Id, location);
        }

        /// <summary>
        /// Clears the location that the user specifies himself at.
        /// </summary>
        public override void ClearUserLocation()
        {
            using (IGatsbDataAdapter adapt = GetDataAdapter())
                adapt.SetUserLocation(CurrentUser.Id, Guid.Empty);
        }

        /// <summary>
        /// Gets the current location of the specified user.
        /// </summary>
        /// <param name="user">The user to get the location for.</param>
        public override LocationCheckin GetUserLocation(User user)
        {
            if (user == null)
                throw new ArgumentNullException("user");

            using (IGatsbDataAdapter adapt = GetDataAdapter())
                return adapt.GetUserCurrentLocation(user.Id);
        }

        /// <summary>
        /// Gets a data adapter that provides access to the database.
        /// </summary>
        /// <returns>The data adapter to use with this application instance.</returns>
        public override IGatsbDataAdapter GetDataAdapter()
        {
            return new GatsbSqlDataAdapter();
        }

        /// <summary>
        /// Gets the <see cref="User"/> with the specified identifier.
        /// </summary>
        /// <param name="userId">The unique identifier of the user.</param>
        /// <returns>The user with the specified identifier.</returns>
        public override User GetUser(Guid userId)
        {
            if (CurrentUser != null && userId == CurrentUser.Id)
                return CurrentUser;

            User usr = null;
            using (IGatsbDataAdapter adapt = GetDataAdapter())
                usr = adapt.GetUser(userId);

            return usr;
        }

        /// <summary>
        /// Gets the location that the unique identifier defines.
        /// </summary>
        /// <param name="locationId">The unique identifier of the location to get.</param>
        /// <returns>The location of the id.</returns>
        public override Location GetLocation(Guid locationId)
        {
            using (IGatsbDataAdapter adapt = GetDataAdapter())
                return adapt.GetLocation(locationId);
        }

        /// <summary>
        /// Creates a new user of the Gatsb application.
        /// </summary>
        /// <param name="user">The user to create.</param>
        /// <param name="password">The password of the user.</param>
        public override void CreateNewUser(User user, HashedPassword password)
        {
            if (user == null)
                throw new ArgumentNullException("user");

            using (IGatsbDataAdapter adapt = GetDataAdapter())
                adapt.CreateUser(user, password);

            CurrentUser = user;
            SendSmsValidation();

            Thread.CurrentPrincipal = new GatsbPrincipal(new GatsbIdentity(user.Id, user.Email.ToString()));
        }

        /// <summary>
        /// Creates a new user of the Gatsb application using temporary credentials,
        /// and does not send an SMS validation..
        /// </summary>
        /// <param name="user">The user to create.</param>
        /// <param name="password">The password of the user.</param>
        public void CreateNewTemporaryUser(User user, HashedPassword password)
        {
            if (user == null)
                throw new ArgumentNullException("user");

            using (IGatsbDataAdapter adapt = GetDataAdapter())
            {
                CreateUserArgs args = new CreateUserArgs();
                args.CreateAsTemporaryUser = true;

                adapt.CreateUser(user, password, args);
            }

            CurrentUser = user;
            Thread.CurrentPrincipal = new GatsbPrincipal(new GatsbIdentity(user.Id, user.Email.ToString()));
        }

        /// <summary>
        /// Attempts to validate a users validation code, such as one sent by the <see cref="SendSmsValidation"/>.
        /// </summary>
        /// <param name="code">The code to validate</param>
        /// <returns>True if validated, or if no code was needed, else false.</returns>
        public override bool TryValidateUserCode(UserValidationCode code)
        {
            if (!UserPresent)
                throw new AuthenticationException("The user is not currently authenticated and cannot be validated.");

            using (IGatsbDataAdapter adapt = GetDataAdapter())
            {
                return adapt.ValidateCode(CurrentUser.Id, code);
            }
        }

        /// <summary>
        /// Authenticates a username and password against the data source.
        /// </summary>
        /// <param name="email">The email address used as a username.</param>
        /// <param name="password">The users hashed password value.</param>
        /// <returns>The authentication result.</returns>
        public override UserLoginResult AuthenticateUser(EmailAddress email, HashedPassword password)
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// Sends the short-messaging-system text message to the <see cref="CurrentUser"/> with
        /// a validation code.
        /// </summary>
        public override void SendSmsValidation()
        {
            if (!UserPresent)
                throw new AuthenticationException("The user is not currently authenticated and cannot be sent an SMS.");

            UserValidationCode code = UserValidationCode.Generate();

            using (IGatsbDataAdapter adapt = GetDataAdapter())
                adapt.AddValidationCode(CurrentUser.Id, code);

            EmailAddress notificationAddress = CurrentUser.TelephoneServiceProvider.CreateSmsAddress(CurrentUser.MobilePhoneNumber);
            throw new NotImplementedException();
            // SendMail(notificationAddress.Address, string.Empty,
            //    string.Format("Thank you for registering at gatsb.com.\r\nYour validation code is '{0}'.", code));
        }

        /// <summary>
        /// Indicates whether or not a <see cref="User"/> has a profile image.
        /// </summary>
        /// <param name="userId">The User ID to check for an image for.</param>
        /// <returns>True when the user has an image, else false.</returns>
        public override bool UserHasProfileImage(Guid userId)
        {
            // Check for the original file.  If it exists, than the user has an image.
            string filePath = Path.Combine(GatsbConfigurationSection.Current.UserUploadedImagesFolder, userId.ToString());
            return File.Exists(filePath);
        }

        /// <summary>
        /// Adds a user as a friend of the <see cref="CurrentUser"/>
        /// </summary>
        /// <param name="userId">The unique identifier of the user to add as a friend.</param>
        public override void AddFriend(Guid userId)
        {
            if (userId == CurrentUser.Id)
                throw new InvalidOperationException("Cannot befriend yourself.");

            using (IGatsbDataAdapter adapt = GetDataAdapter())
                adapt.AddFriend(userId);
        }

        /// <summary>
        /// Rejects a friendship request from a user, or ends a current friendship.
        /// </summary>
        /// <param name="userId">The user to reject the friendship request of</param>
        public override void EndFriendship(Guid userId)
        {
            using (IGatsbDataAdapter adapt = GetDataAdapter())
                adapt.EndFriendship(userId);
        }

        /// <summary>
        /// Gets the status information for the <see cref="CurrentUser"/>.
        /// </summary>
        public override UserStatusInfo GetStatus()
        {
            using (IGatsbDataAdapter adapt = GetDataAdapter())
                return adapt.GetUserStatus();
        }
    }
}
